API GatewayでAPIにIAM認証をかけて、Node.jsでSigV4署名ヘッダを作成してリクエストしてみる
CX事業本部の佐藤です。
API GatewayでIAM認証を設定して、Node.jsでSigV4署名を実装する機会がありましたので、知見としてまとめます。
API Gatewayの認証方式
まずは、API Gatewayの認証方式をおさらいしてみます。API Gatewayでは、APIに対して以下のような認証方式が存在します。
- Cognitoを使った認証
- サードパーティの認証基盤(Auth0など)とLambda Authorizerを使った認証
- IAM認証
この記事では、この中のIAM認証に焦点を当てていきます。
そもそもSigV4署名とは?
正式名称は、AWS Signature V4 署名で、バージョン1〜4まであります。現在はバージョン4を使うのが推奨です。IAMのアクセスキーとハッシュ関数など用いて、AWSへのHTTPリクエストに署名する方法です。普段私たちが使っているAWS SDK
などは、この署名を内部的に設定してくれているため、使う側の私たちはこの署名を意識することはありません。私たちが、SigV4署名を意識する必要があるのは、SDKを使わないでAWSにリクエストする場合、例えば、AWS SDK
がないプログミング言語を使用している場合や、今回のようにAPI GatewayにIAM認証を設定するような場合です。
わざわざ自前で実装しなくても、API GatewayのSDK生成機能を使えばいいのでは?
API GatewayにはSDKの生成機能があり、それを使うことでSDK側がSigV4署名を行ってくれます。ただこのSDKの生成機能は、開発、テスト中にAPIを変更するたびに生成し直す必要があり、生成後は、Lambda Layerの更新や開発リポジトリに組み込むなど、一手間必要なため、今回は自前で実装する選択肢にしました。
Node.jsでSigV4署名を作成する
API GatewayでIAM認証を設定する
まずは、API GatewayでIAM認証を設定したモックAPIを作成します。API Gatewayのコンソールに移動し、APIを作成します。
作成したら、ルートパスを選択しメソッドの作成をクリックします。
POSTを選択してチェックマークをクリックし、Mockを選択し、保存をクリックします。
これで、モックAPIができました。次にIAM認証を設定します。メソッドリクエストをクリックします。
認可で AWS_IAM
を選択します。
これで、モックAPIのIAM認証の設定が完了しました。
SigV4署名ソースコード
次に、SigV4署名をしてHTTPリクエストをしてみます。AWS公式のドキュメントにあるように、SHA256やらHMACとかのハッシュ関数を使ってガリガリ書くのもいいんですが、aws-sdk
のaws-sdk/lib/core
パッケージにSigners.V4
クラスがあり、これを使うことでSigV4署名を作ることができます
以下はNode.jsでSigV4署名を作成するコードです。
const core = require('aws-sdk/lib/core'); const aws = require('aws-sdk'); // アクセスキーとシークレットアクセスキーを設定 const accessKey = 'XXXX'; const secretKey = 'XXXX'; const credential = new aws.Credentials(accessKey, secretKey); main(); function main() { // サービス名は、API GatewayのAPIの場合は、execiute-api固定です。 const serviceName = "execute-api"; // Signers.V4クラスのコンストラクタに渡すオプションを作成します。 const options = { // api gatewayのURL url: 'https://XXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/', headers: {} }; // api gatewayのURLからホスト、パス、クエリストリングを抽出 const parts = options.url.split('?'); const host = parts[0].substr(8, parts[0].indexOf("/", 8) - 8); const path = parts[0].substr(parts[0].indexOf("/", 8)); const querystring = parts[1]; // V4クラスのコンストラクタの引数に沿う形でoptionsを作成 const now = new Date(); options.headers.host = host; options.pathname = () => path; options.methodIndex = 'post'; options.search = () => querystring ? querystring : ""; options.region = 'ap-northeast-1'; options.method = 'POST'; // V4クラスのインスタンスを作成 const signer = new core.Signers.V4(options, serviceName); // SigV4署名 signer.addAuthorization(credential, now); // 署名されたヘッダーを出力 console.log(options.headers); }
上記のコードを実行します。
node index.js
実行すると以下のようなHTTPヘッダの文字列がコンソール上に出力されます。これがSigV4の署名です。このヘッダーを使ってリクエストを送ることでIAM認証を突破することができます。
{ host: 'XXXXXXX.execute-api.ap-northeast-1.amazonaws.com', 'X-Amz-Date': '20200114T105609Z', Authorization: 'AWS4-HMAC-SHA256 Credential=XXXXXX, SignedHeaders=host;x-amz-date, Signature=XXXXXX' }
動作確認
Postmanを使って、API Gatewayにリクエストを送ってみます。まずは、ヘッダに署名を付与しない状態でAPIを叩いてみます。
IAM認証をかけているため、Missing Authentication Token
となりました。
次に、先ほど出力された署名ヘッダを入力して、リクエストを送ってみます。
今度は、Missing Authentication Token
とはでませんでした。うまくいってそうです。
まとめ
最初は、AWS SDKにAPI Gatewayで作成したAPIを叩く関数があって、それを使えば簡単にできるのかなと思っていたんですが、そのような関数はありませんでしたので、今回のようにライブラリを使って実装しました。API GatewayにIAM認証をかける方に参考になれば幸いです。